#include <common.h>
#include <thread.h>
#include <process.h>
#include <vmm.h>
#include <gdt.h>

unsigned int th_list = 0;
unsigned int *bind_thread(thread *t, process *p, int j);
void thread_root(int (*fn)(void*), void *arg)
{
	int retval;
	retval = fn(arg);
	printf("Thread exited with %x", retval);
	current_thread->state = TH_ZOMBIE;
	for(;;);
}

thread *init_threading(unsigned int initial_esp)
{
	next_tid = 1;
	thread *th = (thread *) kmalloc(sizeof(thread));
	th->tid = next_tid++;
	th->state = TH_RUNNING;
	th->time = 0;
	current_thread = th;
	init_processes(initial_esp);
	th->r.useresp = (unsigned int)bind_thread(th,kernel_process,0);
	move_stack(th->r.useresp, initial_esp, STSIZE);
//	th->process = kernel_process;

	return th;
}

thread *new_thread()
{
	thread *th = (thread *)kmalloc(sizeof(thread));
	memset(th,0,sizeof(thread));
	
	th->r.ds = 0x23;
	th->r.ss = 0x23;
	th->r.cs = 0x1B;
	th->r.eflags = 0x3200;
	th->state = TH_NEW;
	th->time = 0;
	return th;
}

unsigned int *bind_thread(thread *t, process *p, int j)
{
	int i = j;
	if(i==0)
	{
	i=0;
	t->stack = 0;
	while(p->threads[i])
		i++;
	}
	if(i==256)
		return 0;
	p->threads[i] = t;
	t->process = p;
	t->stack = STTOP - ( i * STSIZE );
	return (unsigned int *)t->stack;
}

unsigned int unbind_thread(thread *t)
{
	int i=0;
	process *p = t->process;
	asm volatile ("mov %0, %%cr3" : : "r" (p->pd));
	freemap(t->stack);
	asm volatile ("mov %0, %%cr3" : : "r" (current_thread->process->pd));
	while(i<256)
	{
		if (p->threads[i] == t)
			break;
		i++;
	}
	if(i==256)
		return 0;
	p->threads[i] = 0;
	return 1;
}

unsigned int find_binding(thread *t)
{
	int i=0;
	process *p = t->process;
	while(i<256)
	{
		if (p->threads[i] == t)
			break;
		i++;
	}
	return i;
}

thread *create_thread(int (*fn)(void*), void *arg, unsigned int stack_size)
{
	spin(&th_list);
	thread *th = new_thread();
	th->r.useresp = (unsigned int)bind_thread(th,current_process,0);
	map(th->r.useresp & ~0xFFF,pmm_alloc_page(), PAGE_PRESENT | PAGE_WRITE | PAGE_USER);
	th->tid = next_tid++;

	unsigned int *stack = (unsigned int *)th->r.useresp;
	*--stack = (unsigned int)arg;
	*--stack = (unsigned int)fn;
	*--stack = 0;

	th->r.ebp = (unsigned int)stack;
	th->r.useresp = (unsigned int)stack;
	th->r.eip = (unsigned int)&thread_root;
	th_list = 0;
	return th;
}

void destroy_thread(thread *t)
{
	spin(&th_list);
	unbind_thread(t);
	kfree(t);
	th_list = 0;
}


void switch_thread(thread *next, registers *r)
{
	//Save state of current thread
	memcopy(&current_thread->r, r, sizeof(registers));

	if(current_thread->state == TH_RUNNING)
		current_thread->state = TH_READY;
	if(current_thread->process != next->process)
	{
		current_directory = next->process->pd;
		asm volatile ("mov %0, %%cr3" : : "r" (next->process->pd));
		unsigned int cr0;
		asm volatile("mov %%cr0, %0" : "=r" (cr0));
		asm volatile("mov %0, %%cr0" : : "r" (cr0));
	}
	
	current_thread = next;

	//Load state of next thread
	memcopy(r, &current_thread->r, sizeof(registers));
//	printf("\n %x, %x", current_thread->process->pid, current_thread->stack);
	current_thread->state = TH_RUNNING;
}
unsigned int *pd;
thread *signal_thread(unsigned int pid, unsigned int signum, unsigned int sender, registers *r)
{
	spin(&th_list);
	pd = current_directory;
	process *p = (process *)get_process(pid);
	
	thread *th = new_thread();
	th->tid = next_tid++;
	memcopy(&th->r,r,sizeof(registers));
	
	th->r.useresp= (unsigned int)bind_thread(th,p,0);
	
	unsigned int *stack = (unsigned int *)th->r.useresp;
	current_directory = p->pd;
	asm volatile ("mov %0, %%cr3" : : "r" (p->pd));
	map((th->r.useresp & ~0xFFF), pmm_alloc_page(), PAGE_PRESENT | PAGE_WRITE | PAGE_USER);

	*--stack = (unsigned int)sender;
	*--stack = (unsigned int)signum;

	current_directory = current_thread->process->pd;
	asm volatile ("mov %0, %%cr3" : : "r" (current_thread->process->pd));
	
	th->r.ebp = (unsigned int)stack;
	th->r.useresp = (unsigned int)stack;
	th->r.eip = (unsigned int)0xC000;
	
	th->message = current_thread->message;
	
	th_list = 0;
	return th;
}
